﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Linq;
using System.Threading;
using System.Collections.Generic;
using System.IO;

namespace System.Windows.Media.DirectShow
{
    public static class Extensions
    {

        internal static bool ExecuteAction<T>(this IEnumerable list, Action<T> action)
        {
            bool found = false;

            foreach (object item in list)
                if (item is T)
                {
                    try
                    {
                        action((T)item);
                    }
                    catch (ThreadAbortException)
                    { }
                    catch (Exception e)
                    {
                        if (item is IBaseFilter)
                            throw new DirectShowFilterException("Error calling the action", (IBaseFilter)item, e);
                        else
                            throw new DirectShowException("Error calling the action", e);
                    }
                    found = true;
                }

            return found;
        }

        public static void WriteToStream(this Stream source, Stream target, int length, int bufferSize)
        {
            int i = 0;
            byte[] buffer = new byte[bufferSize];

            int c = 0;
            while ((i = source.Read(buffer, 0, Math.Min(bufferSize, length - c))) > 0)
            {
                c += i;
                target.Write(buffer, 0, i);
            }
        }

                /// <summary>
        /// Connect the first disconnected pin of source and target filters
        /// </summary>
        /// <param name="graph"></param>
        /// <param name="sourceFilter"></param>
        /// <param name="targetFilter"></param>
        public static void ConnectFilters(this IFilterGraph graph, IBaseFilter sourceFilter, IBaseFilter targetFilter)
        {
            ConnectFilters(graph, sourceFilter, targetFilter, true);
        }

        /// <summary>
        /// Connect the first disconnected pin of source and target filters
        /// </summary>
        /// <param name="graph"></param>
        /// <param name="sourceFilter"></param>
        /// <param name="targetFilter"></param>
        public static void ConnectFilters(this IFilterGraph graph, IBaseFilter sourceFilter, IBaseFilter targetFilter, bool onlyDisconnected)
        {
            ConnectFilters(graph, sourceFilter, targetFilter, MediaMajorTypes.None, onlyDisconnected);
        }

        /// <summary>
        /// Connect the first disconnected pin of source and target filters
        /// </summary>
        /// <param name="graph"></param>
        /// <param name="sourceFilter"></param>
        /// <param name="targetFilter"></param>
        public static void ConnectFilters(this IFilterGraph graph, IBaseFilter sourceFilter, IBaseFilter targetFilter, MediaType majorType)
        {
            ConnectFilters(graph, sourceFilter, targetFilter, majorType, true);
        }

        /// <summary>
        /// Connect the first disconnected pin of source and target filters
        /// </summary>
        /// <param name="graph"></param>
        /// <param name="sourceFilter"></param>
        /// <param name="targetFilter"></param>
        /// <param name="onlyDisconnected">If true find only pin disconnected. If false it disconect the first matching pin</param>
        public static void ConnectFilters(this IFilterGraph graph, IBaseFilter sourceFilter, IBaseFilter targetFilter, MediaType majorType, bool onlyDisconnected)
        {
            if (sourceFilter == null)
                throw new ArgumentNullException("sourceFilter");
            if (targetFilter == null)
                throw new ArgumentNullException("targetFilter");

            IPin op = sourceFilter.Pins.FindByDirection(PinDirection.Output).First(p => (!onlyDisconnected || p.ConnectedPin == null) && p.MediaTypes.Any(m => m.MajorType == majorType || majorType == MediaMajorTypes.None));
            IPin ip = targetFilter.Pins.FindByDirection(PinDirection.Input).First(p => (!onlyDisconnected || p.ConnectedPin == null) && p.MediaTypes.Any(m => m.MajorType == majorType || majorType == MediaMajorTypes.None));
            if (op.ConnectedPin != null)
                op.Disconnect();
            if (ip.ConnectedPin != null)
                ip.Disconnect();

            graph.ConnectDirect(op, ip, null);
        }

        /// <summary>
        /// Return a tree list as a flat list
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">Base source list</param>
        /// <param name="childPredicate">Predicate to get child list</param>
        /// <returns></returns>
        public static IEnumerable<T> ToFlat<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> childPredicate)
        {
            foreach (T t in source)
            {
                yield return t;

                IEnumerable<T> children = childPredicate(t);
                foreach (T child in ToFlat(children, childPredicate))
                    yield return child;
            }
        }

    }
}
